package utils

import (
	"context"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-resty/resty/v2"
	"github.com/rs/zerolog/log"
	"github.com/spf13/cast"
	"net/url"
	"reflect"
	"time"
)

type HttpV struct {
	request *resty.Request
	Ctx     context.Context
	url     string
	timeOut time.Time
}

type Option func(*HttpV)

func WithSetUrl(url string) Option {
	return func(v *HttpV) {
		v.url = url
	}
}

func New(opt ...Option) *HttpV {
	client := resty.New()
	http := &HttpV{
		request: client.R(),
	}
	for _, i2 := range opt {
		i2(http)
	}
	return http
}

func (h *HttpV) GetRequest() *resty.Request {
	return h.request
}

func (h *HttpV) isExistUrl(url ...string) *HttpV {
	if h.url == "" && len(url) <= 0 {
		panic("未找到地址")
	}
	if len(url) >= 1 {
		h.url = url[0]
	}
	return h
}

func (h *HttpV) POST(url ...string) (*resty.Response, error) {
	return h.isExistUrl(url...).request.Execute(resty.MethodPost, h.url)
}

func (h *HttpV) GET(url ...string) (*resty.Response, error) {
	return h.isExistUrl(url...).request.Execute(resty.MethodGet, h.url)
}

func (h *HttpV) Put(url ...string) (*resty.Response, error) {
	return h.isExistUrl(url...).request.Execute(resty.MethodPut, h.url)
}

func (h *HttpV) Delete(url ...string) (*resty.Response, error) {
	return h.isExistUrl(url...).request.Execute(resty.MethodDelete, h.url)
}

func (h *HttpV) Options(url ...string) (*resty.Response, error) {
	return h.isExistUrl(url...).request.Execute(resty.MethodOptions, h.url)
}

func (h *HttpV) Patch(url ...string) (*resty.Response, error) {
	return h.isExistUrl(url...).request.Execute(resty.MethodPatch, h.url)
}
func (h *HttpV) Head(url ...string) (*resty.Response, error) {
	return h.isExistUrl(url...).request.Execute(resty.MethodHead, h.url)
}

func (h *HttpV) SetQueryParams(params map[string]string) *HttpV {
	for p, v := range params {
		h.request.SetQueryParam(p, v)
	}
	return h
}

func (h *HttpV) SetCtx(ctx context.Context) *HttpV {
	h.Ctx = ctx
	return h
}

func (h *HttpV) SetHeaders(headers map[string]string) *HttpV {
	for hs, v := range headers {
		h.request.SetHeader(hs, v)
	}
	return h
}

func (h *HttpV) SetQueryParamsFromValues(params url.Values) *HttpV {
	for p, v := range params {
		for _, pv := range v {
			h.request.QueryParam.Add(p, pv)
		}
	}
	return h
}

func (h *HttpV) SetFormData(data map[string]interface{}) *HttpV {
	for k, v := range data {
		h.request.FormData.Set(k, cast.ToString(v))
	}
	return h
}

func (h *HttpV) SetBody(body interface{}) *HttpV {
	h.request.Body = body
	return h
}

func (h *HttpV) SetResult(res interface{}) *HttpV {
	if res != nil {
		h.request.Result = getPointer(res)
	}
	return h
}

func valueOf(i interface{}) reflect.Value {
	return reflect.ValueOf(i)
}
func getPointer(v interface{}) interface{} {
	vv := valueOf(v)
	if vv.Kind() == reflect.Ptr {
		return v
	}
	return reflect.New(vv.Type()).Interface()
}

//HttpPost ...
func HttpPost(ctx context.Context,url string, header map[string]string, params map[string]interface{}) ([]byte, error) {
	query := New()
	flag := true
	for _, pv := range header {
		if pv == binding.MIMEPOSTForm {
			query.SetFormData(params)
			flag = false
			break
		}
	}
	if flag {
		query.SetBody(params)
	}
	rsp,err := query.SetHeaders(header).POST(url)
	if err !=nil {
		log.Info().Err(err).Str("sUrl",url).
			Str("sHeader",ToString(header)).
			Str("sParams",ToString(params)).
			Msg("HttpPost msg")
		return nil, err
	}
	body := rsp.Body()
	log.Info().Str("sUrl",url).
		Str("sHeader",ToString(header)).
		Str("sParams",ToString(params)).
		Bytes("sBody",body).
		Msg("HttpPost msg")
	return body, err
}


//HttpGet ...
func HttpGet(ctx context.Context,url string, header map[string]string) ([]byte, error) {
	rsp,err := New().SetHeaders(header).GET(url)
	if err !=nil {
		log.Info().Err(err).Str("sUrl",url).
			Str("sHeader",ToString(header)).
			Msg("HttpGet msg")
		return nil, err
	}
	body := rsp.Body()
	log.Info().Str("sUrl",url).
		Str("sHeader",ToString(header)).
		Bytes("sBody",body).
		Msg("HttpGet msg")
	return body, err
}